package view;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.util.List;


/**
 * This class facilitates the drawing of a graph to the screen. Specifically,
 * this draws the axis, labels, and markers to the screen, allowing subclasses
 * to draw the actual data in a way that is fitting for that data.
 * 
 * @author David Spruill
 *         edited by Wynne Lok
 * 
 */
public abstract class Axis {
    private static final int DEFAULT_NUM_X = 3;
    private static final int DEFAULT_NUM_Y = 3;
    private static final int DEFAULT_MARK_LEN_TIME = 9;
    private static final int DEFAULT_MARK_LEN_DIV = 10;
    private static final int DEFAULT_LABEL_LEN_TIME = 7;
    private static final int DEFAULT_LABEL_LEN_DIV = 10;
    private static final int DEFAULT_LABEL_FONT_SIZE = 12;
    private static final int DEFAULT_LABEL_POINT_SIZE = 4;
    private static final int DRAWSTRING_X_TIMES = 5;
    private static final int DRAWSTRING_Y_DIV = 3;

    private String myXLabel;
    private String myYLabel;

    // variables that will be used by subclasses as well
    private Dimension mySize;
    private Font myLabelFont;
    private int myOffset;
    private int myGraphWidth;
    private int myGraphHeight;
    private int myDataMax;
    private int myDataMaxX;
    private int myDataSize;
    private Color myAxisColor;
    private Color myPointColor;
    private Color myLabelColor;
    private int myPointSize;
    private int myNumberXMarkers;
    private int myNumberYMarkers;
    private int myMarkerLength;
    private int myLabelLength;

    /**
     * Draw the graph with one data set
     * 
     * @param g the graphics object with which to draw
     * @param list the list of data to graph
     */

    public abstract void paint (Graphics g, List<Integer> list);

    /**
     * Draw the graph with two data sets
     * 
     * @param g the graphics object with which to draw
     * @param xData the list of data to graph by the X-axis
     * @param yData the list of data to graph by the Y-axis
     */
    // public abstract void paint (Graphics g, List<Integer> xData,
    // List<Integer> yData);

    /**
     * Finds the maximum of the data (used for scaling)
     * 
     * @param data the list of the data (integers)
     * @return the highest value in the data
     */
    protected int getMax (List<Integer> data) {
        int high = 0;
        for (Integer number : data) {
            if (number > high) {
                high = number;
            }
        }
        return high;
    }

    /**
     * Finds the minimum of the data (used for scaling)
     * 
     * @param data the list of the data (integers)
     * @return the lowest value in the data
     */
    protected int getMin (List<Integer> data) {
        int low = data.get(0);
        for (Integer number : data) {
            if (number < low) {
                low = number;
            }
        }
        return low;
    }

    /**
     * This draws the axis, the axis labels, and the value markers along the
     * axis
     * 
     * @param g the graphics object used to draw to the JFrame
     */
    public void drawAxis (Graphics g) {

        drawAxisLines(g);

        drawAxisLabels(g);

        drawAxisMarkers(g);

    }

    /**
     * Draw the two lines that represent the axis of the graph
     * 
     * @param g
     */
    private void drawAxisLines (Graphics g) {
        g.setColor(myAxisColor);
        g.drawLine(myOffset, myOffset, myOffset, getMySize().height - myOffset);
        g.drawLine(myOffset, getMySize().height - myOffset, getMySize().width - myOffset,
                   getMySize().height - myOffset);
    }

    /**
     * Draw the individual tick marks on the axis, and the corresponding labels,
     * scaled to fit the graph width and height.
     * 
     * @param g
     */
    private void drawAxisMarkers (Graphics g) {

        g.setColor(myAxisColor);
        for (int i = 0; i <= myNumberXMarkers; i++) {
            float change = i / (float) myNumberXMarkers;
            int xChange = myOffset + (int) (change * myGraphWidth);

            g.drawLine(xChange, getMySize().height - myMarkerLength, xChange, getMySize().height -
                                                                              myOffset);

            int xLabel = (int) (change * myDataSize);
            g.drawString("" + xLabel, xChange, getMySize().height - myLabelLength);
        }
        for (int i = 0; i <= myNumberYMarkers; i++) {
            float change = i / (float) myNumberYMarkers;
            int yChange = myOffset + (int) (change * myGraphHeight);

            g.drawLine(myMarkerLength, yChange, myOffset, yChange);

            int yLabel = (int) ((myNumberYMarkers - i) / ((float) myNumberYMarkers) * myDataMax);
            g.drawString("" + yLabel, myLabelLength, yChange);
        }
    }

    /**
     * This draws the two axis labels to the screen
     * 
     * @param g
     */
    private void drawAxisLabels (Graphics g) {
        g.setFont(myLabelFont);
        g.setColor(myLabelColor);
        g.drawString(myXLabel, getMySize().width / 2, getMySize().height - myOffset +
                                                      DRAWSTRING_X_TIMES * myLabelFont.getSize());
        g.drawString(myYLabel, myOffset / DRAWSTRING_Y_DIV, getMySize().height / 2);
    }

    /**
     * Set the label for the X-axis.
     * 
     * @param label label for x axis
     */
    public void setXLabel (String label) {
        myXLabel = label;
    }

    /**
     * Set the label for the Y-axis.
     * 
     * @param label label for y axis
     */
    public void setYLabel (String label) {
        myYLabel = label;
    }

    /**
     * @param axisColor the axisColor to set
     */
    public void setAxisColor (Color axisColor) {
        this.myAxisColor = axisColor;
    }

    /**
     * @param pointColor the pointColor to set
     */
    public void setPointColor (Color pointColor) {
        this.myPointColor = pointColor;
    }

    /**
     * @param labelFont the labelFont to set
     */
    public void setLabelFont (Font labelFont) {
        this.myLabelFont = labelFont;
    }

    /**
     * @param pointSize the pointSize to set
     */
    public void setPointSize (int pointSize) {
        this.myPointSize = pointSize;
    }

    /**
     * @param numberXMarkers the numberXMarkers to set
     */
    public void setNumberXMarkers (int numberXMarkers) {
        this.myNumberXMarkers = numberXMarkers;
    }

    /**
     * @param numberYMarkers the numberYMarkers to set
     */
    public void setNumberYMarkers (int numberYMarkers) {
        this.myNumberYMarkers = numberYMarkers;
    }

    /**
     * @param markerLength the markerLength to set
     */
    public void setMarkerLength (int markerLength) {
        this.myMarkerLength = markerLength;
    }

    /**
     * @param labelLength the labelLength to set
     */
    public void setLabelLength (int labelLength) {
        this.myLabelLength = labelLength;
    }

    /**
     * @param labelColor the labelColor to set
     */
    public void setLabelColor (Color labelColor) {
        this.myLabelColor = labelColor;
    }

    /**
     * This sets the defaults for a graph. They can be changed using the
     * methods above, but by default they are set by a call to this.
     */
    public void setDefaults () {
        myNumberYMarkers = DEFAULT_NUM_Y;
        myNumberXMarkers = DEFAULT_NUM_X;
        myMarkerLength = DEFAULT_MARK_LEN_TIME * myOffset / DEFAULT_MARK_LEN_DIV;
        myLabelLength = DEFAULT_LABEL_LEN_TIME * myOffset / DEFAULT_LABEL_LEN_DIV;
        myLabelFont = new Font("TimesRoman", Font.PLAIN, DEFAULT_LABEL_FONT_SIZE);
        myPointSize = DEFAULT_LABEL_POINT_SIZE;
        myAxisColor = Color.blue;
        myPointColor = Color.red;
    }

    protected Dimension getMySize () {
        return mySize;
    }

    /**
     * Not really sure about the "_____ hides a field" but found that this is
     * because
     * checkstyle does not like parameter name to be the same name as the local
     * variable
     */
    protected void setSize (Dimension size) {
        this.mySize = size;
    }

    protected Font getLabelFont () {
        return myLabelFont;
    }

    protected int getOffset () {
        return myOffset;
    }

    protected void setOffset (int offset) {
        this.myOffset = offset;
    }

    protected int getGraphWidth () {
        return myGraphWidth;
    }

    protected void setGraphWidth (int graphWidth) {
        this.myGraphWidth = graphWidth;
    }

    protected int getGraphHeight () {
        return myGraphHeight;
    }

    protected void setGraphHeight (int graphHeight) {
        this.myGraphHeight = graphHeight;
    }

    protected int getDataMax () {
        return myDataMax;
    }

    protected void setDataMax (int dataMax) {
        this.myDataMax = dataMax;
    }

    protected int getMyDataMaxX () {
        return myDataMaxX;
    }

    protected void setDataMaxX (int dataMaxX) {
        this.myDataMaxX = dataMaxX;
    }

    protected int getMyDataSize () {
        return myDataSize;
    }

    protected void setDataSize (int dataSize) {
        this.myDataSize = dataSize;
    }

    protected Color getMyAxisColor () {
        return myAxisColor;
    }

    protected Color getMyPointColor () {
        return myPointColor;
    }

    protected Color getLabelColor () {
        return myLabelColor;
    }

    protected int getMyPointSize () {
        return myPointSize;
    }

    protected int getNumberXMarkers () {
        return myNumberXMarkers;
    }

    protected int getNumberYMarkers () {
        return myNumberYMarkers;
    }

    protected int getMarkerLength () {
        return myMarkerLength;
    }

    protected int getLabelLength () {
        return myLabelLength;
    }

}
